// Copyright 2021-2024 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {
  DescExtension,
  DescField,
  FieldDescriptorProto_Label,
} from "@bufbuild/protobuf";
import {
  FieldDescriptorProto_Type,
  LongType,
} from "@bufbuild/protobuf";
import type {
  GeneratedFile,
  Printable,
  Schema,
} from "@bufbuild/protoplugin/ecmascript";
import {getFieldDefaultValueExpression, getMessageType} from "./util.js";
import { runtime } from './typescript';

// prettier-ignore
export function generateFieldInfo(schema: Schema, f: GeneratedFile, field: DescField | DescExtension) {
  const {
    FieldInfo,
  } = runtime;
  f.print("    res.push(new ", FieldInfo, "().", getFieldInfoLiteral(schema, field, f), ");");
}

// prettier-ignore
export function getFieldInfoLiteral(schema: Schema, field: DescField | DescExtension, f: GeneratedFile): Printable {
  const e: Printable = [];
  const {
    fiMapInterface,
    makeEnumTypeInstance,
  } = runtime;
  e.push("buildNo(", field.number, ").");
  if (field.kind == "field") {
    e.push(`buildName("`, field.name, `").`);
    if (field.jsonName !== undefined) {
      e.push(`buildJsonName("`, field.jsonName, `").`);
    }
  }
  switch (field.fieldKind) {
    case "scalar":
      e.push(`buildKind("scalar").buildT(`, field.scalar, ").");
      if (field.longType != LongType.BIGINT) {
        e.push("buildL(", field.longType, ").");
      }
      break;
    case "map":
      e.push(`buildKind("map").`);
      e.push("buildK(", field.mapKey, ").");
      switch (field.mapValue.kind) {
        case "scalar":
          e.push("buildV(new ",fiMapInterface,`("scalar", `, field.mapValue.scalar,")).");
          break;
        case "message":``
          e.push("buildV(new ",fiMapInterface,`("message")).`);
          break;
        case "enum":
          e.push("buildV(new ",fiMapInterface,`("enum")).`);
          break;
      }
      break;
    case "message":
      e.push(`buildKind("message").buildT(`, getMessageType(field.message), ".instance).");
      if (field.proto.type === FieldDescriptorProto_Type.GROUP) {
        e.push("buildDelimited(true).");
      }
      break;
    case "enum":
      e.push(`buildKind("enum").buildT(`, makeEnumTypeInstance, `("`, field.enum.typeName, `", `, getMessageType(field.enum), ".enumType)).");
      break;
  }
  if (field.repeated) {
    e.push("buildRepeated(true).");
    if (field.packed !== field.packedByDefault) {
      e.push(`buildRacked(${field.packed}).`);
    }
  }
  if (field.optional) {
    e.push("buildOpt(true).");
  } else if (field.proto.label === FieldDescriptorProto_Label.REQUIRED) {
    e.push("buildReq(true).");
  }
  const defaultValue = getFieldDefaultValueExpression(field);
  if (defaultValue !== undefined) {
    e.push("buildDefaultValue(", defaultValue, ").");
  }
  if (field.oneof) {
    e.push("buildOneOf(new ", runtime.InternalOneofInfo, `("${field.oneof.name}")).`);
  }
  const lastE = e[e.length - 1];
  if (typeof lastE == "string" && lastE.endsWith(".")) {
    e[e.length - 1] = lastE.substring(0, lastE.length - 1);
  }
  return e;
}